//
// Created by l00391713 on 2024/2/29.
//
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
#include <regex>
#include <list>
#include <functional>
#include "client_store_process.hpp"

static void SetProcess(c10d::Store &store, const std::smatch &matcher);
static void CompareAndSetProcess(c10d::Store &store, const std::smatch &matcher);
static void GetProcess(c10d::Store &store, const std::smatch &matcher);
static void AddProcess(c10d::Store &store, const std::smatch &matcher);
static void DelProcess(c10d::Store &store, const std::smatch &matcher);
static void CountProcess(c10d::Store &store, __attribute__((unused)) const std::smatch &matcher);
static void WaitProcess(c10d::Store &store, const std::smatch &matcher);

static const std::list<std::tuple<std::string, std::regex, std::function<void(c10d::Store &, const std::smatch &)>>>
    processors = {
        std::make_tuple("set <key> <value:num>", std::regex{ R"(^set\s+([a-zA-Z0-9._-]+)\s+(\d+)$)" }, SetProcess),
        std::make_tuple("cas <key> <old:num> <new:num>", std::regex{ R"(^cas\s+([a-zA-Z0-9._-]+)\s+(\d+)\s+(\d+)$)" },
    CompareAndSetProcess),
        std::make_tuple("get <key>", std::regex{ R"(^get\s+([a-zA-Z0-9._-]+)$)" }, GetProcess),
        std::make_tuple("add <key> <value:num>", std::regex{ R"(^add\s+([a-zA-Z0-9._-]+)\s+(\d+)$)" }, AddProcess),
        std::make_tuple("del <key>", std::regex{ R"(^del\s+([a-zA-Z0-9._-]+)$)" }, DelProcess),
        std::make_tuple("count", std::regex{ R"(^count$)" }, CountProcess),
        std::make_tuple("wait <key1:key2:...>", std::regex{ R"(^wait\s+([a-zA-Z0-9._:-]+)$)" }, WaitProcess),
    };


void LoopProcessForStore(c10d::Store &store) noexcept
{
    while (true) {
        std::cout << "Enter command(exit to quit) >";
        std::cout.flush();

        std::string input;
        std::getline(std::cin, input);
        if (input == "exit") {
            break;
        }

        if (input == "help") {
            std::cout << "commands:" << std::endl;
            for (const auto &processor : processors) {
                std::cout << "\t" << std::get<0>(processor) << std::endl;
            }
            continue;
        }

        bool found = false;
        std::smatch matcher;
        for (const auto &processor : processors) {
            if (std::regex_match(input, matcher, std::get<1>(processor))) {
                std::get<2>(processor)(store, matcher);
                found = true;
                break;
            }
        }

        if (!found) {
            std::cout << "invalid input: " << input << std::endl;
        }
    }
}
static void SetProcess(c10d::Store &store, const std::smatch &matcher)
{
    std::string key = matcher[1];
    std::string value = matcher[2];
    try {
        store.set(key, std::vector<uint8_t>{ value.begin(), value.end() });
        std::cout << "set key: " << key << " success." << std::endl;
    } catch (const std::runtime_error &e) {
        std::cout << "set key: " << key << " failed: " << e.what() << std::endl;
    }
}

static void CompareAndSetProcess(c10d::Store &store, const std::smatch &matcher)
{
    std::string key = matcher[1];
    std::string expect = matcher[2];
    std::string set = matcher[3];
    try {
        auto result = store.compareSet(key, std::vector<uint8_t>{ expect.begin(), expect.end() },
            std::vector<uint8_t>{ set.begin(), set.end() });
        std::cout << "compare and set for key: " << key << " result=" << std::string(result.begin(), result.end()) <<
            std::endl;
    } catch (const std::runtime_error &e) {
        std::cout << "compare and set for key: " << key << " failed: " << e.what() << std::endl;
    }
}

static void GetProcess(c10d::Store &store, const std::smatch &matcher)
{
    std::string key = matcher[1];
    try {
        auto result = store.get(key);
        std::cout << "get for key: " << key << " result=" << std::string(result.begin(), result.end()) << std::endl;
    } catch (const std::runtime_error &e) {
        std::cout << "get for key: " << key << " failed: " << e.what() << std::endl;
    }
}

static void AddProcess(c10d::Store &store, const std::smatch &matcher)
{
    std::string key = matcher[1];
    std::string value = matcher[2];
    auto delta = std::stol(value);
    try {
        auto result = store.add(key, delta);
        std::cout << "add for key: " << key << " result=" << result << std::endl;
    } catch (const std::runtime_error &e) {
        std::cout << "add for key: " << key << " failed: " << e.what() << std::endl;
    }
}

static void DelProcess(c10d::Store &store, const std::smatch &matcher)
{
    std::string key = matcher[1];
    try {
        auto result = store.deleteKey(key);
        std::cout << "delete for key: " << key << " result=" << result << std::endl;
    } catch (const std::runtime_error &e) {
        std::cout << "delete for key: " << key << " failed: " << e.what() << std::endl;
    }
}

static void CountProcess(c10d::Store &store, const std::smatch &matcher)
{
    try {
        auto result = store.getNumKeys();
        std::cout << "count of key result=" << result << std::endl;
    } catch (const std::runtime_error &e) {
        std::cout << "count of key failed: " << e.what() << std::endl;
    }
}

static void WaitProcess(c10d::Store &store, const std::smatch &matcher)
{
    std::string keys = matcher[1];
    std::vector<std::string> tokens;
    std::istringstream tokenStream(keys);
    std::string token;
    while (std::getline(tokenStream, token, ':')) {
        if (!token.empty()) {
            tokens.emplace_back(token);
        }
    }

    std::stringstream str;
    str << "keys:(";
    bool first = true;
    for (auto &tk: tokens) {
        if (!first) {
            str << ", ";
        }
        str << tk;
        first = false;
    }
    str << ")";
    std::cout << "-- waiting for " << str.str() << " begin --" << std::endl;
    store.wait(tokens);
    std::cout << "-- waiting for " << str.str() << " finished --" << std::endl;
}